#include <avr/io.h>
#include <avr/interrupt.h>
#include "globals.h"
#include "bikirom-ints.h"
#include "layer_uart.h"

// Interrupt Handler structure
typedef struct _typIntHandler {
	unsigned char		last_state;
	enum _eExtIntCtrl	eExtIntCtrl;
	vpIntHandler		handler;
} _typIntHandler;

static _typIntHandler int_handles[eIntMax];		// INT 0-7

void	int_startup(void)
{
	unsigned char	i;
	for (i=0;i<eIntMax;i++)
	{
		int_handles[i].handler = NULL;
		int_handles[i].last_state = 0;
	}
}

void	int_attach( enum _eExtInts	eExtInt, enum _eExtIntCtrl eExtIntCtrl, vpIntHandler Handler)
{
	unsigned char	nInt;

	if (eExtInt < eIntMax)
	{
		// Save the information
		int_handles[eExtInt].eExtIntCtrl = eExtIntCtrl;
		int_handles[eExtInt].handler = Handler;

		// Configure External Interrupts
		if (eExtInt <= eINT7)
		{
			nInt = eExtInt;

			// Disable the interrupt first
			outb(EIMSK, inb(EIMSK) & ~BV(nInt));

			if (Handler != NULL)
			{
				unsigned char nMsk=0;

				switch (eExtIntCtrl)
				{
				case eIntCtrlLow:
					nMsk = 0;
					break;
				case eIntCtrlRising:
					nMsk = BV(ISC00) | BV(ISC01);
					break;
				case eIntCtrlFalling:
					nMsk = BV(ISC01);
					break;
				case eIntCtrlAny:
					nMsk = BV(ISC00);
					break;
				}

				if (nInt >= 4)
				{
					outb(EICRB, (inb(EICRB) & ~(0x3 << ((nInt-4)*2))) | (nMsk << ((nInt-4)*2)));
				}
				else
				{
					outb(EICRA, (inb(EICRA) & ~(0x3 << (nInt*2))) | (nMsk << (nInt*2)));
				}

				// Enable the interrupt
				outb(EIMSK, inb(EIMSK) | BV(nInt));
			}
		}
#ifdef atmega1280
		// Configure Pin Change Interrupts
		else if (eExtInt <= ePINT7)
		{
			nInt = eExtInt - ePINT0;

			// Enable the PCInterrupt
			outb(PCICR, inb(PCICR) | BV(PCIE0));

			int_handles[eExtInt].last_state = inb(PINB) & BV(nInt);

			if (Handler != NULL)
			{
				outb(PCMSK0, inb(PCMSK0) | BV(nInt));
			}
			else
			{
				outb(PCMSK0, inb(PCMSK0) & ~BV(nInt));
			}
		}
#endif
	}
}

#ifdef atmega1280
SIGNAL(SIG_PIN_CHANGE0)
{
	register unsigned char	nInt = 7;
	_typIntHandler* ptypHandler = NULL;

	do
	{
		ptypHandler = &int_handles[ePINT0+nInt];
		// Check if this interrupt is enabled
		if ((inb(PCMSK0) & BV(nInt))
		&& ((inb(PINB) & BV(nInt)) ^ ptypHandler->last_state)
		&& (ptypHandler->handler != NULL))
		{
			ptypHandler->last_state = inb(PINB) & BV(nInt);
			if (ptypHandler->eExtIntCtrl == eIntCtrlAny
			||  (!ptypHandler->last_state
				&& (ptypHandler->eExtIntCtrl == eIntCtrlLow))
			|| (ptypHandler->last_state
				&& (ptypHandler->eExtIntCtrl == eIntCtrlRising))
			|| (!ptypHandler->last_state
				&& (ptypHandler->eExtIntCtrl == eIntCtrlFalling)))
			{
				ptypHandler->handler( ptypHandler->last_state );
			}
		}
	} while (--nInt);

}
#endif

uint8_t	int_trigger( enum _eExtInts	eExtInt )
{
	unsigned char state = 0;

	if (eExtInt <= eINT3)
	{
		state = (inb(PIND)>>eExtInt) & 0x01;
	}
	else if (eExtInt <= eINT7)
	{
		state = (inb(PINE)>>eExtInt) & 0x01;
	}
#ifdef atmega1280
	else
	{
		state = (inb(PINB) >> (eExtInt - ePINT0)) & 0x01;
	}
#endif

	//int_handles[eExtInt].handler(state);

		//uart_send_byte_unbuffered(0, 0x11);

	return state;
}

static void interrupt_check(enum _eExtInts eExtInt, unsigned char state)
{
	// Call the interrupt Handler
	if (int_handles[eExtInt].handler != NULL)
	{
		int_handles[eExtInt].handler(state);
	}
}

SIGNAL(SIG_INTERRUPT0)
{
	interrupt_check(eINT0,inb(PIND) & 0x01);
}
SIGNAL(SIG_INTERRUPT1)
{
	interrupt_check(eINT1,(inb(PIND) >> 1) & 0x01);
}
SIGNAL(SIG_INTERRUPT2)
{
	interrupt_check(eINT2,(inb(PIND) >> 2) & 0x01);
}
SIGNAL(SIG_INTERRUPT3)
{
	interrupt_check(eINT3,(inb(PIND) >> 3) & 0x01);
}
SIGNAL(SIG_INTERRUPT4)
{
	interrupt_check(eINT4,(inb(PINE) >> 4) & 0x01);
}
SIGNAL(SIG_INTERRUPT5)
{
	interrupt_check(eINT5,(inb(PINE) >> 5) & 0x01);
}
SIGNAL(SIG_INTERRUPT6)
{
	interrupt_check(eINT6,(inb(PINE) >> 6) & 0x01);
}
SIGNAL(SIG_INTERRUPT7)
{
	interrupt_check(eINT7,(inb(PINE) >> 7) & 0x01);
}
